﻿#include "FITKGraphObjectArrow.h"

// VTK
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <vtkDoubleArray.h>
#include <vtkGlyph3D.h>
#include <vtkDistanceToCamera.h>
#include <vtkProperty.h>
#include <vtkUnstructuredGrid.h>
#include <vtkArrowSource.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"

namespace Exchange
{
    FITKGraphObjectArrow::FITKGraphObjectArrow()
    {
        // Initialize.
        init();

        // Generate the actor.
        generateGraph();

        // Set the layer need to be rendered.
        setRenderLayer(1, 1);

        // Save if the bounds are fixed or dynamic.
        m_hasFixedBounds = false;
    }

    FITKGraphObjectArrow::~FITKGraphObjectArrow()
    {
        // Delete vtk objects.
        deleteVtkObj(m_arrowSrc);
        deleteVtkObj(m_points);
        deleteVtkObj(m_ugrid);   
    }

    void FITKGraphObjectArrow::init()
    {
        // Create the glyph grid.
        m_ugrid = vtkUnstructuredGrid::New();
        m_points = vtkPoints::New();
        m_ugrid->SetPoints(m_points);

        // Create the arrow source.
        m_arrowSrc = vtkArrowSource::New();
        m_arrowSrc->SetShaftRadius(0);
        m_arrowSrc->SetTipLength(0.4);
        m_arrowSrc->SetTipRadius(0.15);
        m_arrowSrc->SetTipResolution(12);

        // Create the normal and scalars.
        m_normal = vtkDoubleArray::New();
        m_normal->SetNumberOfComponents(3);
        m_scalar = vtkDoubleArray::New();
        m_scalar->SetNumberOfComponents(1);

        // Initialize.
        m_normal->InsertNextTuple3(1., 0., 0.);
        m_scalar->InsertNextValue(1.);
        m_points->InsertNextPoint(0., 0., 0.);

        // Set the normals and scalars.
        m_ugrid->GetPointData()->SetNormals(m_normal);
        m_ugrid->GetPointData()->SetScalars(m_scalar);

        // Initialize the glyph size.
        m_glyphSize = 50.;
    }

    void FITKGraphObjectArrow::setPosition(double* pos)
    {
        if (!m_points || !m_ugrid)
        {
            return;
        }

        // Add the point to the points.
        m_points->Reset();
        m_points->InsertNextPoint(pos);

        // Update the pipeline.
        m_ugrid->Modified();
    }

    void FITKGraphObjectArrow::setPosition(double px, double py, double pz)
    {
        if (!m_points || !m_ugrid)
        {
            return;
        }

        // Add the point to the points.
        m_points->Reset();
        m_points->InsertNextPoint(px, py, pz);

        // Update the pipeline.
        m_ugrid->Modified();
    }

    void FITKGraphObjectArrow::setDirection(double* dir)
    {
        if (!m_normal || !m_ugrid)
        {
            return;
        }

        // Add the point to the points.
        m_normal->Reset();
        m_normal->SetNumberOfComponents(3);
        m_normal->InsertNextTuple3(dir[0], dir[1], dir[2]);

        // Update the pipeline.
        m_ugrid->Modified();
    }

    void FITKGraphObjectArrow::setDirection(double dx, double dy, double dz)
    {
        if (!m_normal || !m_ugrid)
        {
            return;
        }

        // Add the point to the points.
        m_normal->Reset();
        m_normal->SetNumberOfComponents(3);
        m_normal->InsertNextTuple3(dx, dy, dz);

        // Update the pipeline.
        m_ugrid->Modified();
    }

    void FITKGraphObjectArrow::setVisible(bool visibility)
    {
        if (m_fActor)
        {
            m_fActor->SetVisibility(visibility);
        }
    }

    void FITKGraphObjectArrow::update(bool forceUpdate)
    {
        Q_UNUSED(forceUpdate);

        // Update the actor.
        if (m_fActor)
        {
            m_fActor->update();
        }
    }

    void FITKGraphObjectArrow::updateVisibility()
    {
        return;
    }

    bool FITKGraphObjectArrow::isRelatedTo(int dataObjId)
    {
        Q_UNUSED(dataObjId);
        return false;
    }

    bool FITKGraphObjectArrow::getDataVisibility()
    {
        return false;
    }

    void FITKGraphObjectArrow::generateGraph()
    {
        if (!m_ugrid || !m_arrowSrc)
        {
            return;
        }

        // Glyph.
        vtkGlyph3D* glyph = vtkGlyph3D::New();
        vtkDistanceToCamera* distanceToCamera = vtkDistanceToCamera::New();
        distanceToCamera->SetScreenSize(m_glyphSize);
        
        glyph->SetInputConnection(distanceToCamera->GetOutputPort());

        // Add the point array name need to be used.      
#if VTK_MAJOR_VERSION < 8
        glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "DistanceToCamera");
#else
        glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, distanceToCamera->GetDistanceArrayName());
#endif

        glyph->SetScaling(true);
        glyph->SetScaleModeToScaleByScalar();
        glyph->SetVectorModeToUseNormal();
        glyph->SetSourceConnection(m_arrowSrc->GetOutputPort());

        distanceToCamera->SetInputData(m_ugrid);

        // Add to data list.
        m_glyphList.push_back(glyph);
        m_distToCameraList.push_back(distanceToCamera);

        // Create actor.
        m_fActor = FITKGraphActor::New();
        m_fActor->setActorType(ActorType::OtherActor);
        m_fActor->setGraphObject(this);
        m_fActor->setScalarVisibility(false);
        m_fActor->SetPickable(false);
        m_fActor->SetVisibility(false);
        m_fActor->GetProperty()->SetRepresentation(1);
        m_fActor->GetProperty()->SetColor(1., 0., 0.);
        m_fActor->setInputConnection(glyph->GetOutputPort());
        addActor(m_fActor);
    }
}   // namespace Exchange